/*
 * map.c
 *
 *  Created on: 15 nov. 2010
 *      Author: Nicolas Aucouturier, Laurent Reveillere, Vincent Filou
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <SDL.h>
#include <SDL_image.h>

#include <time.h>
#include "../include/constant.h"
#include "../include/map.h"
#include "../include/misc.h"
#include "../include/monster.h"
#include "../include/bomb_list.h"
#include "../include/monster_list.h"

typedef char t_cell_type_;

struct s_map {
	int width;
	int height;

	t_cell_type_* grid;

	SDL_Surface* img_box;
	SDL_Surface* img_goal;
	SDL_Surface* img_bonus_bomb_range_inc;
	SDL_Surface* img_bonus_bomb_range_dec;
	SDL_Surface* img_bonus_bomb_nb_inc;
	SDL_Surface* img_bonus_bomb_nb_dec;
	SDL_Surface* img_bonus_life;
	SDL_Surface* img_key;
	SDL_Surface* img_door;
	SDL_Surface* img_closed_door;
	SDL_Surface* img_stone;
	SDL_Surface* img_tree;
	SDL_Surface* img_bomberwoman;
};

#define CELL(i,j) (i +  map->width * j)

t_map map_new(int width, int height) {
	assert(width > 0 && height > 0);

	t_map map = malloc(sizeof(struct s_map));
	if (map == NULL)
		exit(EXIT_FAILURE);

	map->width = width;
	map->height = height;

	map->grid = (t_cell_type_ *) malloc(height * width * sizeof(t_cell_type_));
	if (map->grid == NULL)
		exit(EXIT_FAILURE);

	// Grid cleaning
	int i, j;
	for (i = 0; i < width; i++) {
		for (j = 0; j < height; j++) {
			map->grid[CELL(i,j)] = CELL_EMPTY;
		}
	}

	// Sprite loading
	map->img_bomberwoman = load_image(IMG_BOMBERWOMAN);
	map->img_tree = load_image(IMG_MAP_TREE);
	map->img_box = load_image(IMG_MAP_CASE);
	map->img_goal = load_image(IMG_MAP_GOAL);
	map->img_key = load_image(IMG_MAP_KEY);
	map->img_stone = load_image(IMG_MAP_STONE);
	map->img_door = load_image(IMG_MAP_DOOR);
	map->img_closed_door = load_image(IMG_MAP_CLOSED_DOOR);
	map->img_bonus_bomb_range_inc = load_image(IMG_BONUS_BOMB_RANGE_INC);
	map->img_bonus_bomb_range_dec = load_image(IMG_BONUS_BOMB_RANGE_DEC);
	map->img_bonus_bomb_nb_inc = load_image(IMG_BONUS_BOMB_NB_INC);
	map->img_bonus_bomb_nb_dec = load_image(IMG_BONUS_BOMB_NB_DEC);
	map->img_bonus_life = load_image(IMG_BONUS_LIFE);
	return map;
}
//check if we are still on the map
int map_is_inside(t_map map, int x, int y) {
	assert(map);
	if (x >= 0 && x < map->width && y >= 0 && y < map->height)
		return 1;
	else
		return 0;
}

void map_free(t_map map) {
	if (map == NULL) {
		return;
	}

	free(map->grid);

	SDL_FreeSurface(map->img_bonus_bomb_nb_dec);
	SDL_FreeSurface(map->img_bonus_bomb_nb_inc);
	SDL_FreeSurface(map->img_bonus_bomb_range_dec);
	SDL_FreeSurface(map->img_bonus_bomb_range_inc);
	SDL_FreeSurface(map->img_closed_door);
	SDL_FreeSurface(map->img_door);
	SDL_FreeSurface(map->img_key);
	SDL_FreeSurface(map->img_stone);
	SDL_FreeSurface(map->img_box);
	SDL_FreeSurface(map->img_goal);
	SDL_FreeSurface(map->img_tree);
	SDL_FreeSurface(map->img_bonus_life);
	SDL_FreeSurface(map->img_bomberwoman);

	free(map);
}

int map_get_width(t_map map) {
	assert(map != NULL);
	return map->width;
}

int map_get_height(t_map map) {
	assert(map != NULL);
	return map->height;
}
//Returns the four first bit
int map_get_cell_shift_type(t_map map, int x, int y) {
	assert(map && map_is_inside(map, x, y));

	return map->grid[CELL(x,y)] >> 4;

}
// Returns the four last bit
int map_get_cell_undertype(t_map map, int x, int y) {
	assert(map && map_is_inside(map, x, y));

	return map->grid[CELL(x,y)] << 4;

}

t_cell_type map_get_cell_type(t_map map, int x, int y) {
	assert(map && map_is_inside(map, x, y));
	return map->grid[CELL(x,y)] & 15;
}

void map_set_cell_type(t_map map, int x, int y, t_cell_type type) {
	assert(map && map_is_inside(map, x, y));
	map->grid[CELL(x,y)] = type;
}

void display_bonus(t_map map, SDL_Surface* screen, SDL_Rect* place,
		t_cell_type_ type) {
	// bonus is encoded with the 4 most significant bits
	switch (type >> 4) {
	case BONUS_BOMB_RANGE_INC:
		SDL_BlitSurface(map->img_bonus_bomb_range_inc, NULL, screen, place);
		break;

	case BONUS_BOMB_RANGE_DEC:
		SDL_BlitSurface(map->img_bonus_bomb_range_dec, NULL, screen, place);
		break;

	case BONUS_BOMB_NB_INC:
		SDL_BlitSurface(map->img_bonus_bomb_nb_inc, NULL, screen, place);
		break;

	case BONUS_BOMB_NB_DEC:
		SDL_BlitSurface(map->img_bonus_bomb_nb_dec, NULL, screen, place);
		break;

	case BONUS_LIFE:
		SDL_BlitSurface(map->img_bonus_life, NULL, screen, place);
		break;
	}
}

void display_scenery(t_map map, SDL_Surface* screen, SDL_Rect* place,
		t_cell_type_ type) {

	switch (type >> 4) {
	case SCENERY_STONE:
		SDL_BlitSurface(map->img_stone, NULL, screen, place);
		break;

	case SCENERY_TREE:
		SDL_BlitSurface(map->img_tree, NULL, screen, place);
		break;
	}
}

void map_display(t_map map, SDL_Surface *screen) {
	int i, j;

	assert(map != NULL);
	assert(screen != NULL);
	assert(map->height > 0 && map->width > 0);

	assert(map->img_bonus_bomb_nb_dec != NULL);
	assert(map->img_bonus_bomb_nb_inc != NULL);
	assert(map->img_bonus_bomb_range_dec != NULL);
	assert(map->img_bonus_bomb_range_inc != NULL);
	assert(map->img_bonus_life != NULL);
	assert(map->img_closed_door != NULL);
	assert(map->img_door != NULL);
	assert(map->img_key != NULL);
	assert(map->img_stone != NULL);
	assert(map->img_box != NULL);
	assert(map->img_goal != NULL);

	for (i = 0; i < map->width; i++) {
		for (j = 0; j < map->height; j++) {
			SDL_Rect place;
			place.x = i * SIZE_BLOC;
			place.y = j * SIZE_BLOC;

			t_cell_type_ type = map->grid[CELL(i,j)];

			switch (type & 15) { // type is encoded with 4 bits (the & 15 (1111)) is a mask to keep the four less significant bits)
			case CELL_GOAL:
				SDL_BlitSurface(map->img_goal, NULL, screen, &place);
				break;
			case CELL_BOMBERWOMAN:
				SDL_BlitSurface(map->img_bomberwoman, NULL, screen, &place);
				break;
			case CELL_SCENERY:
				display_scenery(map, screen, &place, type);
				break;
			case CELL_CASE:
				SDL_BlitSurface(map->img_box, NULL, screen, &place);
				break;
			case CELL_BONUS:
				display_bonus(map, screen, &place, type);
				break;
			case CELL_KEY:
				SDL_BlitSurface(map->img_key, NULL, screen, &place);
				break;
			case CELL_DOOR:
				SDL_BlitSurface(map->img_door, NULL, screen, &place);
				break;
			case CELL_CLOSED_DOOR:
				SDL_BlitSurface(map->img_closed_door, NULL, screen, &place);
				break;
			}
		}

	}
}

//load and read the different map
t_map map_load(const char* map_file) {
	t_map map;

	int x, y, read;

	FILE* file = fopen(map_file, "r");
	assert(file);

	fscanf(file, "%d,%d\n", &x, &y);

	map = map_new(x, y);

	for (int i = 0; i < x; i++) {
		for (int j = 0; j < y; j++) {
			fscanf(file, "%d ", &read);

			map->grid[x * i + j] = read;
		}
		fscanf(file, "\n");
	}
	fclose(file);
	return map;
}
